#include "watcher.h"
#include <strings.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <cstdlib>
#include <cstring>
#include <sys/inotify.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <fcntl.h>
#include "gsyncer.h"

using namespace std;

Watcher::Watcher()
{
	if( (ctl_sock = socket(AF_INET,SOCK_STREAM,0)) < 0 )
	{
		//cerr << "open_udp error" << endl;
		printf("open_udp_error\n");
		exit(-1);
	}


	bzero(&srv_ctl,sizeof(srv_ctl));
	bzero(&srv_tsf,sizeof(srv_tsf));

	init_sock(CTL,&srv_ctl);
	init_sock(TSF,&srv_tsf);

	int	res;

	res = sem_init(&bin_sem,0,0);
	if(res!=0)
	{
	  perror("semaphore initialization failed");
	  exit(-1);
	}

	res = pthread_mutex_init(&work_mutex,NULL);
	if(res!=0)
	{
	  perror("Mutex initialization failed");
	  exit(-1);
	}
	//	usr = _usr;
//	pwd = _pwd;
/*
	if( (sem_id = semget(SEM_KEY, 2, 0666|IPC_CREAT)) < 0 )
	{
		perror("semget");
		exit(-1);
	}

	if( init_semvalue(sem_id, 2) < 0 )
	{
		cerr << "init_semvalue error" << endl;
		exit(-1);
	}
*/
}


void Watcher::set_usr_info(string& usr, string& pwd)
{
	this -> usr = usr;
	this -> pwd = pwd;
}

int Watcher::get_sock(int flag)
{
	if( flag == CTL )
	{
		return ctl_sock;
	} 
	else
	{
	}
}

void Watcher::init_sock(int kind, struct sockaddr_in* sock_addr)
{
	sock_addr->sin_family = AF_INET;

	if( kind == CTL )
	{
		sock_addr->sin_port = htons(SRV_CTL_PT);
	} 
	else
	{
		sock_addr->sin_port = htons(SRV_TSF_PT); 
	}

	if( inet_pton(AF_INET,SRV_IP, &(sock_addr->sin_addr)) < 0 )
	{
		perror("inet_pton");
		exit(1);
	}
}

void Watcher::watch(vector<string>& all_path)
{
	fd_nofity = inotify_init();
	if( fd_nofity < 0 )
	{
		perror("inotify_inint");
		exit(-1);
	}

	uint32_t wd;

	//对所有gsyncer文件夹中的子文件夹进行监控 
	for( int i = 0 ; i < all_path.size() ; i++ )
	{
		if( (wd = inotify_add_watch(fd_nofity,all_path[i].c_str(), 
						IN_ALL_EVENTS)) < 0 )
		{
			perror("add_watch");
			exit(-1);
		}
		
		watch_list[wd] = all_path[i].c_str(); 
	}
	
	int res;
	struct inotify_event* event;
	char buf[1024];
	struct event* e_ptr;

	int i=0;
	while( true )
	{
	    
		res = read(fd_nofity,buf,1024);
		if( res < 0 )
		{
			perror("read");
			continue;
		}

		while(i < res)
		{
			event = (struct inotify_event *) &buf[i];
			//如果是目录自己的变动，就抛弃
			if( event->len == 0 )
			{
				break;
			}

			//过滤临时文件
			if(file_filter(event->name) == true )
			{
				break;
			}

			if( event->mask & IN_CREATE )
			{
			    
				//如果是目录 则加入MAP并通知服务器
				//if( is_dir(event->wd,event->name) )
		        if(event->mask & IN_ISDIR)
				{
					uint32_t wd_temp;
					string name_temp = watch_list[event->wd] + "/";
					name_temp += event->name;

					if( (wd_temp = inotify_add_watch(fd_nofity,name_temp.c_str(), 
								IN_ALL_EVENTS)) < 0 )
					{
						perror("add_watch");
						exit(-1);
					}
		
					watch_list[wd_temp] = name_temp.c_str(); 
					
					build_event(CREATE,name_temp,e_ptr);
					//cout<<"creat dir "<<name_temp<<endl;
					printf("creat dir %s\n",name_temp.c_str());
					//sem_wait(sem_id,MUTEX);
					pthread_mutex_lock(&work_mutex);
					event_queue.push(e_ptr);
					//sem_signal(sem_id,MUTEX);
					pthread_mutex_unlock(&work_mutex);
					//sem_signal(sem_id,QUEUE_N);
					sem_post(&bin_sem);
					
					//	print_event(event->wd,event->name,CREATE);
				}
				else
				{
				    //file is created 
				    string	name_temp  = watch_list[event->wd] + "/";
				    name_temp		  += event->name;
				    build_event(CREATE_F,name_temp,e_ptr);
				    //cout<<"create file "<<name_temp<<endl;
				    printf("creat file %s\n",name_temp.c_str());
						
				    //sem_wait(sem_id,MUTEX);
				    pthread_mutex_lock(&work_mutex);
				    event_queue.push(e_ptr);
				    //sem_signal(sem_id,MUTEX);
				    pthread_mutex_unlock(&work_mutex);
				    //sem_signal(sem_id,QUEUE_N);
				    sem_post(&bin_sem);
				    
				}

			}
			if( event->mask & IN_DELETE || event->mask & IN_MOVED_FROM)
			{
				uint32_t wd_temp;
				string name_temp = watch_list[event->wd] + "/";
				name_temp += event->name;

				/*bool is_dir_ = false;
				map<int,string>::iterator map_itr;
				for( map_itr = watch_list.begin(); map_itr != watch_list.end() ;map_itr++ )
				{
					if(map_itr->second == name_temp)
					{
						wd_temp = map_itr->first;
						is_dir_ = true;
							break;
					}
				}
				*/
				if( event->mask & IN_ISDIR )
				{
					//if( inotify_rm_watch(fd_nofity,wd_temp) < 0 )
					//{
					//	perror("inotify_rm_watch");
					//	exit(-1);
					//}
				    //这里需要处理删除监控
					build_event(DEL_D,name_temp,e_ptr);
					//	cout<<"del dir "<<name_temp<<endl;
					printf("del dir %s\n ",name_temp.c_str());	
				}
				else
				{
					build_event(DEL_F,name_temp,e_ptr);
					//cout<<"del file "<<name_temp<<endl;
					printf("del file %s\n",name_temp.c_str());
				}

				//sem_wait(sem_id,MUTEX);
				pthread_mutex_lock(&work_mutex);
				event_queue.push(e_ptr);
				//sem_signal(sem_id,MUTEX);
				pthread_mutex_unlock(&work_mutex);
				//sem_signal(sem_id,QUEUE_N);
				sem_post(&bin_sem);
				
				
				
				//print_event(event->wd,event->name,DELETE);
			}
			if( event->mask & IN_CLOSE_WRITE || event->mask & IN_MODIFY )
			{
				string name_temp = watch_list[event->wd] + "/";
				name_temp += event->name;
				build_event(UP_F,name_temp,e_ptr);
				
				//cout<<"modify or close write file "<<name_temp<<endl;
				printf("modify or close write file %s\n",name_temp.c_str());
				//sem_wait(sem_id,MUTEX);
				pthread_mutex_lock(&work_mutex);
				event_queue.push(e_ptr);
				//sem_signal(sem_id,MUTEX);
				pthread_mutex_unlock(&work_mutex);
				//sem_signal(sem_id,QUEUE_N);
				sem_post(&bin_sem);
				
				

				//print_event(event->wd,event->name,UPDATE);
			}
			if( event->mask & IN_MOVED_TO )
			{
				string name_temp = watch_list[event->wd] + "/";
				name_temp += event->name;

				
				if(event->mask & IN_ISDIR )
				{
					vector<string> moved_path;
					moved_path.push_back(name_temp);
					dir_search(moved_path,name_temp);
					add_watch(moved_path);
					string path=watch_list[event->wd] ;
					chdir(path.c_str());
					string f_name = event->name;	
					string cmd = "zip -rq \"/tmp/";
					cmd += f_name;
					cmd += ".zip\" \"";
					cmd += f_name;
					cmd+="\"";
					
					system(cmd.c_str());
					chdir("-");
					build_event(UP_D,name_temp,e_ptr);
					//cout<<"zip-cmd "<<cmd<<endl;
					//cout<<"in moved to dir "<<name_temp<<endl;
					printf("zip-cmd %s\n",cmd.c_str());
					printf("in moved to dir %s \n",name_temp.c_str());
				}
				else
				{
				    //cout<<"in moved to file "<<name_temp<<endl;
				    printf("in moved to file %s \n",name_temp.c_str());
				    build_event(UP_F,name_temp,e_ptr);
				}

				
				//sem_wait(sem_id,MUTEX);
				pthread_mutex_lock(&work_mutex);
				event_queue.push(e_ptr);
				//sem_signal(sem_id,MUTEX);
				pthread_mutex_unlock(&work_mutex);
				//sem_signal(sem_id,QUEUE_N);
				sem_post(&bin_sem);
				
				//print_event(event->wd,event->name,UPDATE);
			}

			i = i + sizeof(struct inotify_event) + event->len;
		}

		i = 0;
	}
}


void Watcher::add_watch(vector<string>& all_path)
{
	uint32_t wd;

	for( int i = 0 ; i < all_path.size() ; i++ )
	{
		wd = inotify_add_watch(fd_nofity,all_path[i].c_str(),IN_ALL_EVENTS);
		if( wd < 0 )
		{
			perror("inotify_add");
			exit(-1);
		}

		watch_list[wd] = all_path[i];
	}
	
}

//int Watcher::file_delete(const char* name)
//{
//	struct udp_pkt_2* pkt = new struct udp_pkt_2;

	//打包
//	strncpy(pkt->file,name,FILE_LEN);
//	strncpy(pkt->user,"ace",USR_LEN);
//	pkt->type = 3;

	
//	delete pkt;
//}

bool Watcher::file_filter(const char* file)
{
	//文件过滤器  过滤各种编辑器产生的临时文件
	if( strcmp(file,"4913") == 0 )
	{
		return true;
	}

	if( file[0] == '.' )
	{
		return true;
	}

	if( file[strlen(file)-1] == '~' )
	{
		return true;
	}

	return false;
}

void Watcher::print_event(struct event* event)
{

	//cout << "----" << endl;
	//cout << event->path << " ";
	printf("-------\n");
	printf("%s",event->path.c_str());

	switch ( event->option )
	{
		case CREATE :
			//cout << "creat";
			printf("creat");
			break;
		case DEL_F :
			//cout << "delete";
			printf("delete");
			break;
		case UP_F :
			//cout << "update";
			printf("update");
		default :
			break;
	}

	//cout << endl << "----" << endl;
	printf("\n --------------\n");
}

bool Watcher::is_dir(int wd, const char* name)
{
	string name_temp = watch_list[wd] + "/";
	name_temp += name;

	struct stat buf;
	
//	cout << name_temp << endl;
	if( stat(name_temp.c_str(),&buf) < 0 )
	{
		perror("stat");
		exit(-1);
	}

	return S_ISDIR(buf.st_mode);
}

void Watcher::build_event(int option, string& path, struct event*& event)
{
	event = new struct event;
	event->option = option;
	event->path = path;
}

void Watcher::cre_or_del(int option, struct event* event)
{
	struct event_info	info;
	strncpy(info.path,(event->path).c_str(),PATH_LEN);
	info.type = event->option;
	Writen(ctl_sock,&info,sizeof(info));
	print_event(event);
}

void Watcher::cexit()
{
	struct event * p = new struct event;
	p -> option = EXIT;

	
	//sem_wait(sem_id,MUTEX);
	pthread_mutex_lock(&work_mutex);
	event_queue.push(p);
	//sem_signal(sem_id,MUTEX);
	pthread_mutex_unlock(&work_mutex);
	//sem_signal(sem_id,QUEUE_N);
	sem_post(&bin_sem);
	
}

void Watcher::process_events()
{
	struct event* event;

	while( true )
	{

	  sem_wait(&bin_sem);
	  //sem_wait(sem_id,MUTEX);
	  pthread_mutex_lock(&work_mutex);
	  
	  event = event_queue.front();
	  event_queue.pop();
	  //sem_signal(sem_id,MUTEX);
	  pthread_mutex_unlock(&work_mutex);
	  //sem_signal(sem_id,QUEUE_N);
	  
	  
	  switch ( event->option ){
	  case CREATE :
	    cre_or_del(CREATE,event);
	    break;
	  case CREATE_F:
	    cre_or_del(CREATE_F,event);
	    break;
	  case DEL_F :
	    cre_or_del(DEL_F,event);
	    break;
	  case DEL_D :
	    cre_or_del(DEL_D,event);
	    break;
	  case UP_F :
	    update(event);
	    break;
	  case UP_D :
	    update(event);
	    break;
	  case EXIT :
	    //cre_or_del(EXIT,event);
	    //shutdown(ctl_sock,SHUT_WR);
	    exit(0);
	    break;
	  default :
	    break;
	  }
	  delete event;
	}
}

void Watcher::update(struct event* event)
{
	//检查文件是否还存在  防止不必要的上传  可以用access函数重写
    int fd_temp = open((event->path).c_str(),O_RDONLY);
    if( fd_temp < 0 ){
      //cout<<"file "<<event->path<<" not exists nowkkkkkkkkkkkkkkk \n";
      printf("file %s not exists nowkkkkkkkkkkkkkkkkkk\n",event->path.c_str());
      close(fd_temp);
      return;
    }
    close(fd_temp);
    //cout<<"come into update"<<endl;
    struct event_info event_info;
    event_info.type = event->option;

    //这里把文件名路径信息传送过去，那边检查，如果不存在，就mkdir
    strncpy(event_info.path,(event->path).c_str(),PATH_LEN);
    Writen(ctl_sock,&event_info,sizeof(event_info));
    
    uint8_t reply;
    read(ctl_sock,&reply,sizeof(uint8_t));
    if( reply != 1 )
	{
	  //  cerr << "reply error" << endl;
	  //  cout<<reply<<endl;
	 printf("reply error\n");
	 printf("%d\n",reply);
	    exit(-1);
	}
    
    int tsf_sock = socket(AF_INET,SOCK_STREAM,0);
    if( connect(tsf_sock, (SA*)&srv_tsf, sizeof(srv_tsf)) < 0 )
	{
	    perror("connect");
	    exit(-1);
	}
    //cout<<"after file path check "<<endl;
    printf("after file path check \n");
    //打包
    struct update_head head;
    head.type = event->option;
    strncpy(head.usr, usr.c_str(),USR_LEN);
    strncpy(head.pwd, pwd.c_str(),PWD_LEN);
    strncpy(head.path, (event->path).c_str(),PATH_LEN);
    
    if( event->option == UP_F )
	{
	    int fd_temp = open((event->path).c_str(),O_RDONLY);
	    if( fd_temp < 0 )
		{
		    //cout<<"file "<<event->path<<" not exists now \n";
		    printf("file %s not exists now \n",event->path.c_str());
		    return ;
		    //cout << event->path << endl;
		    //perror("open");
		    //exit(-1);
		}
	    Writen(tsf_sock,&head, sizeof(head));
	    
	    file_copy(tsf_sock,fd_temp);
	    close(fd_temp);
	    close(tsf_sock);
	} 
    else
	{
		event->path+=".zip";
		strncpy(head.path, (event->path).c_str(),PATH_LEN);
	
		int i;
		for( i=(event->path).length()-1;i>=0;--i)
		{
			if((event->path)[i]=='/')
				break;
		}
	
		string file_name = (event->path).substr(i+1,(event->path).length()-i-1);
		string path = "/tmp/";
		path+=file_name;
		int fd_temp = open((path).c_str(),O_RDONLY);
	
		if( fd_temp < 0 )
	    {
			//cout<<"zip file "<<event->path<<" not exists now \n";
			printf("zip file %s not exists now \n",event->path.c_str());
			return ;
			//cout << event->path << endl;
			//perror("open");
			//exit(-1);
	    }
		
		Writen(tsf_sock,&head, sizeof(head));
	
		file_copy(tsf_sock,fd_temp);
	
		close(fd_temp);
		close(tsf_sock);
		unlink(path.c_str());
    }
}
